package com.github.interview.service.impl;

import com.github.interview.constant.EquityConsts;
import com.github.interview.model.EquityPosition;
import com.github.interview.model.EquityTransaction;
import com.github.interview.persistence.EquityPositionMapper;
import com.github.interview.persistence.EquityTransactionMapper;
import com.github.interview.service.EquityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Objects;

/**
 * @author Stephen
 * @date 2020/6/15 16:44
 */
@Service("equityService")
public class EquityServiceImpl implements EquityService {

    @Autowired
    private EquityTransactionMapper equityTransactionMapper;

    @Autowired
    private EquityPositionMapper equityPositionMapper;

    //must be in a transaction when doing this
    @Transactional
    @Override
    public void addTransaction(EquityTransaction transaction) {
        //if transaction does not exist, the operation will fail
        if (EquityConsts.UPDATE == transaction.getOptType() ||
            EquityConsts.CANCEL == transaction.getOptType()) {
            EquityTransaction query = new EquityTransaction();
            query.setTradeId(transaction.getTradeId());
            query.setOptType(transaction.getOptType());
            EquityTransaction tx = equityTransactionMapper.selectOne(query);
            Objects.requireNonNull(tx, "transaction not existed, operation failed");
        }
        updatePosition(transaction);
        setVersion(transaction);
        equityTransactionMapper.insert(transaction);

    }

    private void updatePosition(EquityTransaction transaction) {
        EquityPosition position = new EquityPosition();
        position.setTradeId(transaction.getTradeId());
        position.setSecurityCode(transaction.getSecurityCode());
        position.setCreateTime(new Date());
        position.setModifyTime(new Date());

        if (EquityConsts.INSERT == transaction.getOptType()) {
            if (EquityConsts.BUY == transaction.getTradeType())
                position.setQuantity(transaction.getQuantity());
            else
                position.setQuantity(-transaction.getQuantity());
        }

        if (EquityConsts.UPDATE == transaction.getOptType()) {
            EquityTransaction query = new EquityTransaction();
            query.setTradeId(transaction.getTradeId());
            query.setVersion(transaction.getVersion()-1);
            EquityTransaction equityTransaction = equityTransactionMapper.selectOne(query);
            Objects.requireNonNull(equityTransaction, "transaction not existed");
            if (EquityConsts.BUY == transaction.getTradeType())
                position.setQuantity(transaction.getQuantity()-equityTransaction.getQuantity());
            else
                position.setQuantity(equityTransaction.getQuantity()-transaction.getQuantity());
        }

        if (EquityConsts.CANCEL == transaction.getOptType()) {
            //get the transaction list with asc order of version
            List<EquityTransaction> list = equityTransactionMapper.selectList(transaction.getTradeId());
            Objects.requireNonNull(list, "transaction not existed");
            //get the first element of the list
            EquityTransaction tx = list.get(0);
            if (EquityConsts.BUY == tx.getTradeType())
                position.setQuantity(list.get(list.size()-1).getQuantity());
            else
                position.setQuantity(-list.get(list.size()-1).getQuantity());
        }

        EquityPosition equityPosition = equityPositionMapper.selectOne(position);

        if (Objects.isNull(equityPosition))
            equityPositionMapper.insert(position);
        else {
            equityPosition.setQuantity(equityPosition.getQuantity()+position.getQuantity());
            equityPosition.setModifyTime(new Date());
            equityPositionMapper.updateByPrimaryKey(equityPosition);
        }
    }

    private void setVersion(EquityTransaction transaction) {
        //get the max version through trade_id
        Integer version = equityTransactionMapper.selectMaxVersion(transaction.getTradeId());
        if (version != null)
            transaction.setVersion(version + 1);
        else
            transaction.setVersion(1);
    }
}
